package de.plushnikov.intellij.plugin.processor.clazz.log;

import com.dtdream.commons.logger.annotations.Slf4jEx;
import com.dtdream.commons.logger.loggers.LoggerType;
import com.google.common.base.Throwables;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import de.plushnikov.intellij.plugin.problem.ProblemBuilder;
import de.plushnikov.intellij.plugin.psi.LombokLightFieldBuilder;
import de.plushnikov.intellij.plugin.util.PsiAnnotationSearchUtil;
import de.plushnikov.intellij.plugin.util.PsiAnnotationUtil;
import de.plushnikov.intellij.plugin.util.PsiClassUtil;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;

/**
 * Created by zhangh on 2017/3/22.
 *
 * @author : zhangh@dtdream.com
 * @version : 1.0.0
 * @since : 1.8
 */
public class Slf4jExProcessor extends AbstractLogProcessor {
  private static final String STRING_NONE = "";

  public Slf4jExProcessor() {
    super(Slf4jEx.class, STRING_NONE, STRING_NONE, STRING_NONE);
  }

  private Set<LoggerType> getLoggerTypes(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation) {
    final Collection<String> includes = PsiAnnotationUtil.getAnnotationValues(psiAnnotation,
      "includes", String.class);
    final Collection<String> excludes = PsiAnnotationUtil.getAnnotationValues(psiAnnotation,
      "excludes", String.class);
    final Set<LoggerType> types = new HashSet<LoggerType>(LoggerType.values().length);
    for (String a : includes)
      types.add(LoggerType.valueOf(a));
    if (types.size() == 0)
      Collections.addAll(types, LoggerType.values());
    for (String a : excludes)
      types.remove(LoggerType.valueOf(a));
    if (PsiAnnotationSearchUtil.isAnnotatedWith(psiClass, Slf4j.class))
      types.remove(LoggerType.RUNTIME);
    return types;
  }

  @Override
  protected void generatePsiElements(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, @NotNull List<? super PsiElement> target) {
    final Collection<PsiField> psiFields = PsiClassUtil.collectClassFieldsIntern(psiClass);
    final Set<String> fields = new HashSet<String>(psiFields.size());
    for (PsiField field : psiFields)
      fields.add(field.getName());
    for (Object e : target)
      if (e instanceof PsiField)
        fields.add(((PsiField) e).getName());
    for (LoggerType type : getLoggerTypes(psiClass, psiAnnotation)) {
      if (fields.contains(type.field))
        continue;
      final Project project = psiClass.getProject();
      final PsiManager manager = psiClass.getContainingFile().getManager();
      final PsiElementFactory psiElementFactory = JavaPsiFacade.getElementFactory(project);
      final PsiType psiLoggerType = psiElementFactory.createTypeFromText(type.type, psiClass);
      LombokLightFieldBuilder loggerField = new LombokLightFieldBuilder(manager, type.field, psiLoggerType)
        .withContainingClass(psiClass)
        .withModifier(PsiModifier.FINAL)
        .withModifier(PsiModifier.PRIVATE)
        .withNavigationElement(psiAnnotation);
      if (isLoggerStatic(psiClass)) {
        loggerField.withModifier(PsiModifier.STATIC);
      }
      final String loggerInitializerParameter;
      final String topic = PsiAnnotationUtil.getStringAnnotationValue(psiAnnotation, "topic");
      if (StringUtil.isEmptyOrSpaces(topic)) {
        loggerInitializerParameter = psiClass.getName() + ".class";
      } else {
        loggerInitializerParameter = '"' + topic + '"';
      }
      final String getLogger = type.getLogger + "(" + loggerInitializerParameter + ")";
      final PsiExpression initializer = psiElementFactory.createExpressionFromText(getLogger, psiClass);
      loggerField.setInitializer(initializer);
      target.add(loggerField);
    }
  }

  @Override
  protected boolean validate(@NotNull PsiAnnotation psiAnnotation, @NotNull PsiClass psiClass, @NotNull ProblemBuilder builder) {
    for (LoggerType type : getLoggerTypes(psiClass, psiAnnotation)) {
      if (psiClass.isInterface() || psiClass.isAnnotationType()) {
        builder.addError("@Slf4jEx is legal only on classes and enums");
        return false;
      }
      final Collection<PsiField> psiFields = PsiClassUtil.collectClassFieldsIntern(psiClass);
      for (PsiField psiField : psiFields) {
        if (type.field.equals(psiField.getName())) {
          builder.addError("Not generating field %s: A field with same name already exists", type.field);
          return false;
        }
      }
    }
    return true;
  }
}
